import { reactive, ref, watch } from 'vue';

let TreeCheckMod = /*#__PURE__*/function (TreeCheckMod) {
  TreeCheckMod["FULL"] = "FULL";
  TreeCheckMod["HALF"] = "HALF";
  TreeCheckMod["CHILD"] = "CHILD";
  TreeCheckMod["SHALLOW"] = "SHALLOW";
  return TreeCheckMod;
}({});
class TreeStore {
  data = [];
  flatData = [];
  keyField = 'id';
  titleField = 'title';
  constructor(props, emit) {
    this.props = props;
    this.emit = emit;
    this.checkable = props.checkable ?? false;
    this.checkRelation = props.checkRelation ?? 'related';
    this.draggable = props.draggable ?? false;
    this.keyField = props.keyField || 'id';
    this.titleField = props.titleField || 'title';
    this.mode = props.mode ?? TreeCheckMod.HALF;
    const store = reactive({
      nodeMap: {},
      nodeList: []
    });
    this.store = store;
    const selectedKey = ref('');
    const selected = ref(props.selected ?? '');
    const value = ref(props.modelValue ?? []);
    this.selectedKey = selectedKey;
    this.selected = selected;
    this.value = value;

    // 外部修改selected值进行同步
    watch(() => props.selected, val => {
      if (val) {
        this.selectNode(val, true);
      }
    }, {
      immediate: true
    });

    // 外部修改selected值进行同步
    watch(() => props.modelValue, val => {
      if (val && val.length) {
        this.setCheckedByMod(val);
      }
    }, {
      immediate: true
    });
  }
  init(data) {
    this.data = data;
    this.flatData = this.getAllFlatNodes(this.data);
    this.store.nodeMap = {};
    this.store.nodeList = [];
    this.buildRelation(this.data, null, 0);
    this.setRootFlatNodes();
    if (this.checkable) {
      const val = this.value.value;
      this.setCheckedByMod(val);
    } else {
      const nodeId = this.selectedKey.value;
      this.selectNode(nodeId, true);
    }
  }

  /**
   * 构建父子关系和层级关系
   * @param data
   * @param parent
   * @param level
   */
  buildRelation = (data, parent, level) => {
    data.forEach(item => {
      this.store.nodeMap[item[this.keyField]] = item;
      item._parent = parent;
      item._level = level;
      item.visible = true;
      if (item.children) {
        this.buildRelation(item.children, item, level + 1);
      }
    });
  };
  setRootFlatNodes = () => {
    const list = this.getFlatNodes(this.data);
    this.store.nodeList = list;
  };

  /**
   * 获取显示的树节点
   * @param nodes
   * @returns
   */
  getFlatNodes = nodes => {
    const list = nodes.flatMap(item => {
      if (item.expand && item.children?.length && item.visible) {
        return [item, this.getFlatNodes(item.children)].flat();
      } else {
        return item.visible ? [item] : [];
      }
    });
    return list;
  };

  /**
   * 获取显示的树节点
   * @param nodes
   * @returns
   */
  getAllFlatNodes = nodes => {
    const list = nodes.flatMap(item => {
      if (item.children?.length) {
        return [item, this.getAllFlatNodes(item.children)].flat();
      } else {
        return [item];
      }
    });
    return list;
  };
  getStore() {
    return this.store;
  }
  openCloseNode = node => {
    this.store.nodeMap[node[this.keyField]].expand = !node.expand;
    this.setRootFlatNodes();
  };

  /**
   * 选择节点
   * @param node
   * @param silence 不触发回调
   */
  selectNode = (nodeId, silence) => {
    const node = this._getNode(nodeId);
    if (node) {
      if (this.store.nodeMap[this.selectedKey.value]) {
        this.store.nodeMap[this.selectedKey.value]._selected = false;
      }
      this.store.nodeMap[node[this.keyField]]._selected = true;
      this.selectedKey.value = node[this.keyField];
      this.selected.value = node[this.keyField];
      this.emit('update:selected', node[this.keyField]);
      !silence && this.emit('nodeSelect', node);
    }
  };
  clearSelect = () => {
    this.store.nodeMap[this.selectedKey.value]._selected = false;
    this.selectedKey.value = '';
    this.selected.value = '';
    this.emit('update:selected', '');
  };
  _storeNode = (node, map) => {
    map[node[this.keyField]] = node;
    node.visible = true;
    if (node.children && node.children?.length > 0) {
      node.children.forEach(item => {
        this._storeNode(item, map);
      });
    }
  };
  storeNode = node => {
    const map = {};
    this._storeNode(node, map);
    const flatData = this.getAllFlatNodes([node]);
    this.flatData = this.flatData.concat(flatData);
    for (const key in map) {
      this.store.nodeMap[key] = map[key];
    }
  };
  getNode = key => {
    return this.store.nodeMap[key];
  };
  _getNode = nodeId => {
    let node;
    if (typeof nodeId === 'string' || typeof nodeId === 'number') {
      node = this.store.nodeMap[nodeId];
    } else {
      node = nodeId;
      nodeId = node[this.keyField];
    }
    return node;
  };
  removeNode = node => {
    if (node) {
      const parent = node._parent;
      if (parent) {
        const index = parent.children.findIndex(item => item[this.keyField] === node[this.keyField]);
        this.store.nodeMap[parent[this.keyField]].children?.splice(index, 1);
      } else {
        // 在根目录或是新节点
        const index = this.data.findIndex(item => item[this.keyField] === node[this.keyField]);
        if (index > -1) {
          this.data.splice(index, 1);
        }
      }
    }
  };
  remove = nodeId => {
    const node = this._getNode(nodeId);
    this.removeNode(node);
  };
  beforeNodeOperation = (targetKey, nodeId) => {
    if (!this.store.nodeMap[targetKey]) {
      console.error('targetKey not exist');
      return false;
    }
    const node = this._getNode(nodeId);
    // 新节点需要将节点和子节点存入map
    if (!this.store.nodeMap[node[this.keyField]]) {
      this.storeNode(node);
    } else {
      // 删除节点
      this.removeNode(node);
    }
    return node;
  };
  append = (parentKey, nodeId) => {
    const node = this.beforeNodeOperation(parentKey, nodeId);
    if (!node) {
      return;
    }
    console.log(this.store.nodeMap, parentKey);
    const target = this.store.nodeMap[parentKey];
    if (!target.children) {
      target.children = [];
    }
    const n = this.store.nodeMap[node[this.keyField]];
    this.updateLevels(node, target._level);
    n._parent = target;
    target.children.push(n);
    if (this.checkable) {
      this.updateNodeCheckStatus(target);
    }
    this.setRootFlatNodes();
  };

  /**
   * 更新节点选择状态
   * @param nodeId
   */
  updateNodeCheckStatus = nodeId => {
    if (!nodeId) {
      return;
    }
    const node = this._getNode(nodeId);
    if (node) {
      const n = this.store.nodeMap[node[this.keyField]];
      n.checked = this.getNodeChecked(n);
      if (this.checkRelation === 'related') {
        this.setCheckedForwardUp(node);
      }
    }
  };
  updateLevels = (node, startLevel) => {
    const n = this.store.nodeMap[node[this.keyField]];
    n._level = startLevel + 1;
    if (node.children && node.children.length > 0) {
      node.children.forEach(item => {
        this.updateLevels(item, startLevel + 1);
      });
    }
  };
  prepend = (parentKey, nodeId) => {
    const node = this.beforeNodeOperation(parentKey, nodeId);
    if (!node) {
      return;
    }
    const target = this.store.nodeMap[parentKey];
    if (!target.children) {
      target.children = [];
    }
    const n = this.store.nodeMap[node[this.keyField]];
    this.updateLevels(node, target._level);
    n._parent = target;
    target.children.unshift(n);
    // 更新状态
    if (this.checkable) {
      this.updateNodeCheckStatus(target);
    }
    this.setRootFlatNodes();
  };
  insertBefore = (targetKey, nodeId) => {
    const node = this.beforeNodeOperation(targetKey, nodeId);
    if (!node) {
      return;
    }
    const target = this.store.nodeMap[targetKey];
    const parent = target._parent || {
      children: this.data
    };
    const targetIndex = parent.children.findIndex(item => item[this.keyField] === targetKey);
    parent.children.splice(targetIndex, 0, node);
    const n = this.store.nodeMap[node[this.keyField]];
    this.updateLevels(node, target._level - 1);
    n._parent = target._parent;

    // 更新状态
    if (this.checkable) {
      this.updateNodeCheckStatus(target._parent);
    }
    this.setRootFlatNodes();
  };
  insertAfter = (targetKey, nodeId) => {
    const node = this.beforeNodeOperation(targetKey, nodeId);
    if (!node) {
      return;
    }
    const target = this.store.nodeMap[targetKey];
    const parent = target._parent || {
      children: this.data
    };
    const targetIndex = parent.children.findIndex(item => item[this.keyField] === targetKey);
    parent.children.splice(targetIndex + 1, 0, node);
    const n = this.store.nodeMap[node[this.keyField]];
    this.updateLevels(node, target._level - 1);
    n._parent = target._parent;

    // 更新状态
    if (this.checkable) {
      this.updateNodeCheckStatus(target._parent);
    }
    this.setRootFlatNodes();
  };
  filter = (keyword, filterMethod) => {
    const defaultFilterMethod = (keyword, node) => {
      const title = node[this.titleField];
      if (title == null || !title.toString) return false;
      return title.toString().toLowerCase().indexOf(keyword.toLowerCase()) > -1;
    };
    filterMethod = filterMethod || defaultFilterMethod;

    // 使用树形结构数据进行遍历
    const filterVisibleNodes = [];
    const visibleMap = {};
    this.flatData.forEach(node => {
      visibleMap[node[this.keyField]] = node._parent && visibleMap[node._parent[this.keyField]] || filterMethod(keyword, node);
      const n = this.store.nodeMap[node[this.keyField]];
      n.visible = visibleMap[n[this.keyField]];
      if (visibleMap[node[this.keyField]]) {
        filterVisibleNodes.push(node);
      }
    });

    // 对于临时列表中的节点，都是可见的，因此将它们的父节点都设为可见并展开
    filterVisibleNodes.forEach(node => {
      const stack = [];
      let parent = node._parent;
      while (parent) {
        stack.unshift(parent);
        parent = parent._parent;
      }
      stack.forEach(parent => {
        const p = this.store.nodeMap[parent[this.keyField]];
        p._filterVisible = true;
        p.visible = (!p._parent || p._parent.visible) && p._filterVisible;
      });
      const n = this.store.nodeMap[node[this.keyField]];
      n.visible = !n._parent || n._parent.visible;
    });
    this.setRootFlatNodes();
  };
  getNodeIndexInShow = nodeId => {
    const node = this._getNode(nodeId);
    if (!node || !node.visible) return -1;
    const index = this.store.nodeList.findIndex(n => n[this.keyField] === node[this.keyField]);
    return index;
  };
  expandAll = () => {
    this.flatData.forEach(item => {
      if (item.visible && item.children && !item.expand) {
        const n = this.store.nodeMap[item[this.keyField]];
        n.expand = true;
      }
    });
    this.setRootFlatNodes();
  };
  collapseAll = () => {
    this.flatData.forEach(item => {
      if (item.children && item.expand) {
        const n = this.store.nodeMap[item[this.keyField]];
        n.expand = false;
      }
    });
    this.setRootFlatNodes();
  };
  checkNode = (nodeId, checked) => {
    const node = this._getNode(nodeId);
    const n = this.store.nodeMap[node[this.keyField]];
    n.checked = checked;
    if (this.checkRelation === 'related') {
      this.setCheckedForwardDown(n, checked);
      n.checked = this.getNodeChecked(node);
      this.setCheckedForwardUp(n);
    }
    const checkedKeys = this.getCheckedKeys(this.mode);
    this.value.value = checkedKeys;
    this.emit('update:modelValue', checkedKeys);
    this.emit('change', checkedKeys);
  };
  setCheckedForwardDown = (node, checked) => {
    if (node.children) {
      node.children.forEach(item => {
        if (item.disabled) return;
        const n = this.store.nodeMap[item[this.keyField]];
        n.checked = checked;
        this.setCheckedForwardDown(item, checked);
      });
    }
  };
  getNodeChecked = nodeId => {
    const node = this._getNode(nodeId);
    if (!node.children || node.children.length === 0) {
      return node.checked;
    } else {
      let checked = false;
      let checkedNum = 0;
      let indeterminateNum = 0;
      node.children.forEach(item => {
        if (item.checked === true) {
          checkedNum++;
        }
        if (item.checked === 'indeterminate') {
          indeterminateNum++;
        }
      });
      if (checkedNum === node.children.length) {
        checked = true;
      } else if (checkedNum > 0) {
        checked = 'indeterminate';
      }
      if (!checked && indeterminateNum > 0) {
        checked = 'indeterminate';
      }
      return checked;
    }
  };
  setCheckedForwardUp = node => {
    const parentNode = node._parent;
    if (parentNode) {
      const checked = this.getNodeChecked(parentNode);
      const n = this.store.nodeMap[parentNode[this.keyField]];
      n.checked = checked;
      this.setCheckedForwardUp(parentNode);
    }
  };
  rename = (nodeId, title) => {
    const node = this._getNode(nodeId);
    this.store.nodeMap[node[this.keyField]][this.titleField] = title;
  };
  expandNode = (nodeId, expand) => {
    const node = this._getNode(nodeId);
    this.store.nodeMap[node[this.keyField]].expand = expand;
    if (expand) {
      this.emit('nodeExpand', node);
    } else {
      this.emit('nodeCollapse', node);
    }
    this.setRootFlatNodes();
  };
  checkAll = () => {
    for (const key in this.store.nodeMap) {
      this.store.nodeMap[key].checked = true;
    }
    const checkedKeys = this.getCheckedKeys(this.mode);
    this.value.value = checkedKeys;
    this.emit('update:modelValue', checkedKeys);
    this.emit('change', checkedKeys);
  };
  uncheckAll = () => {
    for (const key in this.store.nodeMap) {
      this.store.nodeMap[key].checked = false;
    }
    const checkedKeys = this.getCheckedKeys(this.mode);
    this.value.value = checkedKeys;
    this.emit('update:modelValue', checkedKeys);
    this.emit('change', checkedKeys);
  };
  loadData = async (node, loadDataMethod) => {
    const n = this.store.nodeMap[node[this.keyField]];
    n.__loading = true;
    try {
      const list = await loadDataMethod(node);
      if (list.length > 0) {
        list.forEach(item => {
          this.append(node[this.keyField], item);
        });
      }
    } catch (e) {
      //
    } finally {
      n.__loading = false;
    }
    n.loading = false;
  };

  /**
   *
   * @param mode
   * @returns
   */
  getChecked = (mode = TreeCheckMod.HALF) => {
    if (this.checkRelation === 'related') {
      if (mode === TreeCheckMod.FULL) {
        return this.getFullChecked();
      }
      if (mode === TreeCheckMod.CHILD) {
        return this.getChildChecked();
      }
      if (mode === TreeCheckMod.HALF) {
        return this.getHalfChecked();
      }
      if (mode === TreeCheckMod.SHALLOW) {
        return this.getShallowChecked();
      }
    } else {
      return this.getFullChecked();
    }
    return [];
  };

  /**
   * 获取所有选中的节点包含父节点和子节点
   * @returns
   */
  getFullChecked = () => {
    return this.flatData.filter(node => node.checked === true);
  };

  /**
   * 选中的子节点
   * @returns
   */
  getChildChecked = () => {
    return this.flatData.filter(node => node.checked === true && (!node.children || node.children.length === 0));
  };

  /**
   * 返回全部选中子节点和部分选中的父节点
   * @returns
   */
  getHalfChecked = () => {
    return this.flatData.filter(node => node.checked === true || node.checked === 'indeterminate');
  };

  /**
   * 如果父节点下所有子节点全部选中，只返回父节点
   * @returns
   */
  getShallowChecked = () => {
    const ret = [];
    this.flatData.forEach(node => {
      if (node.checked === true) {
        const parentChecked = (() => {
          const parent = node._parent;
          if (!parent) {
            return false;
          }
          return parent.checked === true;
        })();
        if (!parentChecked) {
          ret.push(node);
        }
      }
    });
    return ret;
  };

  /**
   * 选中的节点标识
   * @param mode
   * @returns
   */
  getCheckedKeys = (mode = TreeCheckMod.HALF) => {
    const nodes = this.getChecked(mode);
    return nodes.map(node => node[this.keyField]);
  };
  setNodeDragging = (nodeId, dragging) => {
    const node = this._getNode(nodeId);
    if (node) {
      this.store.nodeMap[node[this.keyField]]._dragging = dragging;
    }
  };
  clearChecked = () => {
    this.flatData.forEach(node => {
      const n = this.store.nodeMap[node[this.keyField]];
      if (n) {
        n.checked = false;
      }
    });
  };
  setCheckedByMod = val => {
    this.clearChecked();
    if (this.checkRelation === 'related') {
      if (this.mode === TreeCheckMod.FULL) {
        this.setCheckedByFull(val);
      }
      if (this.mode === TreeCheckMod.HALF) {
        this.setCheckedByHalf(val);
      }
      if (this.mode === TreeCheckMod.CHILD) {
        this.setCheckedByChild(val);
      }
      if (this.mode === TreeCheckMod.SHALLOW) {
        this.setCheckedByShallow(val);
      }
    } else {
      this.setCheckedByFull(val);
    }
  };
  setCheckedByFull = val => {
    val.forEach(key => {
      const n = this.store.nodeMap[key];
      if (n) {
        n.checked = true;
        this.checkRelation === 'related' && this.setCheckedForwardUp(n);
      }
    });
  };
  setCheckedByHalf = val => {
    val.forEach(key => {
      const node = this._getNode(key);
      if (!node.children || node.children.length === 0) {
        const n = this.store.nodeMap[key];
        if (n) {
          n.checked = true;
          this.checkRelation === 'related' && this.setCheckedForwardUp(n);
        }
      }
    });
  };
  setCheckedByChild = val => {
    val.forEach(key => {
      const node = this._getNode(key);
      if (!node.children || node.children.length === 0) {
        const n = this.store.nodeMap[key];
        if (n) {
          n.checked = true;
          this.checkRelation === 'related' && this.setCheckedForwardUp(n);
        }
      }
    });
  };
  setCheckedByShallow = val => {
    val.forEach(key => {
      const n = this.store.nodeMap[key];
      if (n) {
        n.checked = true;
        this.checkRelation === 'related' && this.setCheckedForwardUp(n);
        this.checkRelation === 'related' && this.setCheckedForwardDown(n, true);
      }
    });
  };
}

export { TreeCheckMod, TreeStore };
